
#include "ModuleFactory.hpp"
#include "FiFoBuffer.hpp"
#include "BroadcastBusInterface.hpp"

#include "Log.hpp"

#include "Statistics.hpp"



NamespaceBegin(BroadcastBusFifoInterface)

using namespace BroadcastBus ;
using namespace FiFo ;





char const*const	kStateIdle				=	"Idle" ;
char const*const	kStateWaitingForBus		=	"Waiting for Bus" ;
char const*const	kStateTransferring		=	"Transferring" ;
char const*const	kStateTransferFailed	=	"Transfer Failed" ;










struct	WritableBusMappedFiFoSettings : public Settings
{
	std::string	busMasterName ;
	std::string	fifoName ;
	unsigned	publicationIdentifier ;
	unsigned	burstSize ;
	unsigned	capacity ;
	SCInterval	fifoDelay ;
	bool		useReliableCommunications ;
	bool		useFullWidth ;
	
	WritableBusMappedFiFoSettings ( std::string const & name )
	:	Settings ( name )
	,	busMasterName()
	,	fifoName()
	,	publicationIdentifier(0)
	,	burstSize(0)
	,	capacity(0)
	,	fifoDelay(0,kSCIntervalSeconds)
	,	useReliableCommunications(true)
	,	useFullWidth(false)
		{
		add ( "busName" , busMasterName ) ;
		add ( "fifoName" , fifoName ) ;
		add ( "publicationIdentifier" , publicationIdentifier ) ;
		add ( "burstLength" , burstSize ) ;
		add ( "capacity" , capacity ) ;
		add ( "fifoDelay" , fifoDelay ) ;
		add ( "useReliableCommunications" , useReliableCommunications ) ;
		add ( "useFullWidth" , useFullWidth ) ;
		}
	virtual void validate ()
		{
		// If using full width, force even
		if ( useFullWidth )
			burstSize &= ~1 ;
		
		
		if ( busMasterName.size() == 0 )
			ThrowException ( "WritableBroadcastBusMappedFiFoSettings" , mInstanceName << ": Bus Master name has zero-length" , __FILE__,__LINE__ ) ;
		if ( fifoName.size() == 0 )
			ThrowException ( "WritableBroadcastBusMappedFiFoSettings" , mInstanceName << ": Fifo name has zero-length" , __FILE__,__LINE__ ) ;
		
		if ( burstSize == 0 )
			ThrowException ( "WritableBroadcastBusMappedFiFoSettings" , mInstanceName << ": Burst-size is zero" , __FILE__,__LINE__ ) ;
		if ( capacity == 0 )
			ThrowException ( "WritableBroadcastBusMappedFiFoSettings" , mInstanceName << ": Capacity is zero" , __FILE__,__LINE__ ) ;
		}
} ;



struct	WritableBusMappedFiFo : public ConfiguredModel<WritableBusMappedFiFoSettings>
{
	SC_HAS_PROCESS(WritableBusMappedFiFo) ;
	
	sc_port<PublisherInterface>	mpBus ;
	FiFo::Buffer				mFiFo ;
	Publication<WriteInterface>	mFiFoPublisher ;
	
	Stats::Value<char const*>	mState ;
	Stats::Accumulator<UInt32>	mTotalData ;
	
	WritableBusMappedFiFo	( ModelVariant const & variant )
	:	ConfiguredModel<WritableBusMappedFiFoSettings> ( variant )
	,	mpBus ()
	,	mFiFo ( sc_module_name((variant.instanceName+"_fifo").c_str()) , mConfig.capacity , mConfig.fifoDelay )
	,	mFiFoPublisher ( )
	,	mState ( variant.instanceName+".state" , kStateIdle )
	,	mTotalData ( variant.instanceName+".totalDataTransferred" , "Words" )
		{
		Log::states("WritableBroadcastBusMappedFiFo") << identity << " Constructed" ;
		
		// Publish the FiFo
		mFiFoPublisher	=	mConfig.fifoName ;
		mFiFoPublisher	=	& mFiFo ;
		
		// Connect the bus interface
		Subscription<PublisherInterface>	pWrite	( mConfig.busMasterName ) ;
		bindPortToInterface ( &mpBus , pWrite ) ;
		
		// Make our thread
		SC_THREAD ( writerRunLoop ) ;
		}
	
	void writerRunLoop ()
		{
		while ( 1 )
			{
			mState = kStateIdle ;
			
			if ( mFiFo.availableLength() >= mConfig.burstSize )
				{
				Log::states("WritableBroadcastBusMappedFiFo") << identity << " We have [enough] available data. Transmitting" ;
				
				Length	count	=	mConfig.useFullWidth ? mConfig.burstSize/2 : mConfig.burstSize ;
				
				PublicationInfo info ( mConfig.publicationIdentifier , count ) ;
				
				mState = kStateWaitingForBus ;
				Flags response = mpBus -> enqueueBroadcastAndWait ( info ) ;
				
				if ( mConfig.useReliableCommunications ? response==kFlagReceivedAcceptance : response&kFlagReceivedAcceptance )
					{
					mState = kStateTransferring ;
					
					mpBus -> commitWriteOperation () ;
					
					if ( mConfig.useFullWidth )
						{
						Log::states("WritableBroadcastBusMappedFiFo") << "Sending 2x32b per 64b bus transmission" ;
						while ( count -- )
							{
							UInt64Breakout b ;
							b.as2xUInt32.a = mFiFo.read() ;
							b.as2xUInt32.b = mFiFo.read() ;
							mpBus -> write ( b.asUInt64 ) ;
							mTotalData += 2 ;
							}
						}
					else
						{
						Log::states("WritableBroadcastBusMappedFiFo") << "Sending 1x32b per 64b bus transmission" ;
						while ( count -- )
							{
							mpBus -> write ( mFiFo.read() ) ;
							mTotalData += 1 ;
							}
						}
					
					mState = kStateIdle ;
					wait ( mFiFo.wasWrittenEvent() ) ;
					}
				else
					{
					mState . setValue ( kStateTransferFailed , Stats::kRetroactive ) ;
					Log::warning("WritableBroadcastBusMappedFiFo") << "No acknowledgement was received upon write attempt. Will retry later. Response was: " << (int)response ;
					}
				}
			else
				{
				mState = kStateIdle ;
				wait ( mFiFo.wasWrittenEvent() ) ;
				}
			}
		}
} ;

TypedModuleMaker<WritableBusMappedFiFo>	gWritableBusMappedFiFoBuilder	( "broadcastBusMappedFiFo.publisher" ) ;






















struct	ReadableBusMappedFiFoSettings : public Settings
{
	std::string	busSlaveName ;
	std::string	fifoName ;
	unsigned	baseAddress ;
	unsigned	capacity ;
	SCInterval	fifoDelay ;
	bool		useFullWidth ;
	
	ReadableBusMappedFiFoSettings ( std::string const & name )
	:	Settings ( name )
	,	busSlaveName()
	,	fifoName()
	,	baseAddress(0)
	,	capacity(0)
	,	fifoDelay(0,kSCIntervalSeconds)
	,	useFullWidth(false)
		{
		add ( "busName" , busSlaveName ) ;
		add ( "fifoName" , fifoName ) ;
		add ( "publicationIdentifier" , baseAddress ) ;
		add ( "capacity" , capacity ) ;
		add ( "fifoDelay" , fifoDelay ) ;
		add ( "useFullWidth" , useFullWidth ) ;
		}
	virtual void validate ()
		{
		if ( busSlaveName.size() == 0 )
			ThrowException ( "ReadableBroadcastBusMappedFiFoSettings" , "Bus Slave name has zero-length" , __FILE__,__LINE__ ) ;
		if ( fifoName.size() == 0 )
			ThrowException ( "ReadableBroadcastBusMappedFiFoSettings" , "Fifo name has zero-length" , __FILE__,__LINE__ ) ;
		if ( capacity == 0 )
			ThrowException ( "ReadableBroadcastBusMappedFiFoSettings" , "Capacity is zero" , __FILE__,__LINE__ ) ;
		}
} ;


struct	ReadableBusMappedFiFo : public ConfiguredModel<ReadableBusMappedFiFoSettings>
{
	SC_HAS_PROCESS(ReadableBusMappedFiFo) ;
	
	sc_port<SubscriberInterface>		mpBus ;
	FiFo::Buffer				mFiFo ;
	Publication<WriteInterface>	mFiFoPublisher ;
	
	Stats::Value<char const*>	mState ;
	Stats::Accumulator<UInt32>	mTotalData ;
	
	ReadableBusMappedFiFo	( ModelVariant const & variant )
	:	ConfiguredModel<ReadableBusMappedFiFoSettings> ( variant )
	,	mpBus ()
	,	mFiFo ( sc_module_name((variant.instanceName+"_fifo").c_str()) , mConfig.capacity , mConfig.fifoDelay )
	,	mFiFoPublisher ( )
	,	mState ( variant.instanceName+".state" , kStateIdle )
	,	mTotalData ( variant.instanceName+".totalDataTransferred" , "Words" )
		{
		// Publish the FiFo
		mFiFoPublisher	=	mConfig.fifoName ;
		mFiFoPublisher	=	& mFiFo ;
		
		// Connect the bus interface
		Subscription<SubscriberInterface>	pWrite	( mConfig.busSlaveName ) ;
		bindPortToInterface ( &mpBus , pWrite ) ;
		
		// Make our thread
		SC_THREAD ( readerRunLoop ) ;
		}
	
	void readerRunLoop ()
		{
		while ( 1 )
			{
			mState = kStateIdle ;
			
			PublicationInfo info ;
			
			mState = kStateWaitingForBus ;
			mpBus -> waitForPublicationInfo ( info ) ;
			
			
			Log::states("ReadableBroadcastBusMappedFiFo") << identity << " Received request" ;
			
			/** Determine if it's ours **/
			if  ( info.topicIdentifier() == mConfig.baseAddress )
				{
				if ( info.burstLength() > mFiFo.availableCapacity() )
					{
					mpBus -> raiseObjection () ;
					Log::states("ReadableBroadcastBusMappedFiFo") << identity << " It's ours, but it's too big for our buffer" ;
					}
				else
					{
					mState = kStateTransferring ;
					
					mpBus -> acknowledgePublication () ;
					
					Log::states("ReadableBroadcastBusMappedFiFo") << identity << " I've got this! I can handle it!" ;
					
					/** Read the data and put it in the fifo **/
					
					Length	count	=	info.burstLength() ;
					if ( mConfig.useFullWidth )
						{
						while ( count -- )
							{
							UInt64Breakout b ;
							mpBus -> read ( b.asUInt64 ) ;
							mFiFo.write ( b.as2xUInt32.a ) ;
							mFiFo.write ( b.as2xUInt32.b ) ;
							mTotalData += 2 ;
							}
						}
					else
						{
						while ( count -- )
							{
							Datum w ;
							mpBus -> read ( w ) ;
							mFiFo.write ( w ) ;
							mTotalData += 1 ;
							}
						}
					}
				}
			else
				{
				Log::states("ReadableBroadcastBusMappedFiFo") << identity << " It's not ours" ;
				}
			
			/** Repeat. Ready, go! **/
			}
		}
} ;

TypedModuleMaker<ReadableBusMappedFiFo>	gReadableBusMappedFiFoBuilder	( "broadcastBusMappedFiFo.subscriber" ) ;








NamespaceEnd(BroadcastBusFifoInterface)

















